//! Copyright (c) 2005-2008, Simon Howard
//! Permission to use, copy, modify, and/or distribute this software for any purpose
//! with or without fee is hereby granted, provided that the above copyright notice
//! and this permission notice appear in all copies.
//! THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
//! REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
//! FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
//! INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
//! LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
//! OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
//! PERFORMANCE OF THIS SOFTWARE.

/// Hash function for pointers.
pub fn pointer_hash<T>(ptr: &T) -> usize {
    ptr as *const T as usize
}

/// Hash function for integers.
pub fn int_hash(value: &i32) -> usize {
    *value as usize
}

/// Case-sensitive hash function for strings.
pub fn string_hash(s: &str) -> usize {
    s.chars().fold(0, |acc, c| acc.wrapping_add(c as usize))
}

/// Case-insensitive hash function for strings.
pub fn string_nocase_hash(s: &str) -> usize {
    s.chars().fold(0, |acc, c| {
        acc.wrapping_add(c.to_ascii_lowercase() as usize)
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    const NUM_TEST_VALUES: usize = 200;

    #[test]
    fn test_pointer_hash() {
        let array = [0; NUM_TEST_VALUES];

        for i in 0..NUM_TEST_VALUES {
            for j in (i + 1)..NUM_TEST_VALUES {
                assert_ne!(pointer_hash(&array[i]), pointer_hash(&array[j]));
            }
        }
    }

    #[test]
    fn test_int_hash() {
        let array = (0..NUM_TEST_VALUES as i32).collect::<Vec<_>>();

        for i in 0..NUM_TEST_VALUES {
            for j in (i + 1)..NUM_TEST_VALUES {
                assert_ne!(int_hash(&array[i]), int_hash(&array[j]));
            }
        }

        let i = 5000;
        let j = 5000;
        assert_eq!(int_hash(&i), int_hash(&j));
    }

    #[test]
    fn test_string_hash() {
        let test1 = "this is a test";
        let test2 = "this is a tesu";
        let test3 = "this is a test ";
        let test4 = "this is a test";
        let test5 = "This is a test";

        assert_ne!(string_hash(test1), string_hash(test2));
        assert_ne!(string_hash(test1), string_hash(test3));
        assert_ne!(string_hash(test1), string_hash(test5));
        assert_eq!(string_hash(test1), string_hash(test4));
    }

    #[test]
    fn test_string_nocase_hash() {
        let test1 = "this is a test";
        let test2 = "this is a tesu";
        let test3 = "this is a test ";
        let test4 = "this is a test";
        let test5 = "This is a test";

        assert_ne!(string_nocase_hash(test1), string_nocase_hash(test2));
        assert_ne!(string_nocase_hash(test1), string_nocase_hash(test3));
        assert_eq!(string_nocase_hash(test1), string_nocase_hash(test5));
        assert_eq!(string_nocase_hash(test1), string_nocase_hash(test4));
    }
}
